package simpledb;

import Zql.*;
import jline.ArgumentCompletor;
import jline.ConsoleReader;
import jline.SimpleCompletor;
import simpledb.common.Database;
import simpledb.common.DbException;
import simpledb.common.Type;
import simpledb.execution.*;
import simpledb.optimizer.LogicalPlan;
import simpledb.optimizer.TableStats;
import simpledb.storage.IntField;
import simpledb.storage.StringField;
import simpledb.storage.Tuple;
import simpledb.storage.TupleDesc;
import simpledb.transaction.Transaction;
import simpledb.transaction.TransactionId;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Parser 是一个 SQL 解析器类，主要用于将用户输入的 SQL 查询语句解析为数据库系统可以理解的逻辑计划（Logical Plan）
 * 并支持构建连接（Join）和过滤条件（Filter）。
 */
public class Parser {

    // 是否开启 explain 模式（输出执行计划信息）
    static boolean explain = false;

    /**
     * 将字符串形式的操作符转换为 Predicate.Op 枚举类型
     *
     * @param s 输入操作符字符串，如 "=", ">", "<", "<>", 等
     * @return 对应的 Predicate.Op 枚举值
     * @throws simpledb.ParsingException 如果操作符不被支持
     */
    public static Predicate.Op getOp(String s) throws simpledb.ParsingException {
        if (s.equals("="))
            return Predicate.Op.EQUALS;
        if (s.equals(">"))
            return Predicate.Op.GREATER_THAN;
        if (s.equals(">="))
            return Predicate.Op.GREATER_THAN_OR_EQ;
        if (s.equals("<"))
            return Predicate.Op.LESS_THAN;
        if (s.equals("<="))
            return Predicate.Op.LESS_THAN_OR_EQ;
        if (s.equals("LIKE"))
            return Predicate.Op.LIKE;
        if (s.equals("~"))
            return Predicate.Op.LIKE;
        if (s.equals("<>"))
            return Predicate.Op.NOT_EQUALS;
        if (s.equals("!="))
            return Predicate.Op.NOT_EQUALS;

        throw new simpledb.ParsingException("未知谓词操作符: " + s);
    }

    /**
     * 处理 WHERE 子句中的表达式，生成逻辑计划中的 Join 或 Filter 节点
     *
     * @param tid 当前事务 ID
     * @param wx  表达式对象（ZExpression）
     * @param lp  当前正在构建的逻辑计划对象
     * @throws simpledb.ParsingException 如果表达式格式不被支持或语法错误
     * @throws IOException               如果读取数据失败
     * @throws ParseException            如果解析过程中出错
     */
    void processExpression(TransactionId tid, ZExpression wx, LogicalPlan lp) throws simpledb.ParsingException,
                                                                             IOException, ParseException {

        // 如果是 AND 连接的多个条件，递归处理每一个子条件
        if (wx.getOperator().equals("AND")) {
            for (int i = 0; i < wx.nbOperands(); i++) {
                if (!(wx.getOperand(i) instanceof ZExpression)) {
                    throw new simpledb.ParsingException("嵌套查询目前不支持。");
                }
                ZExpression newWx = (ZExpression) wx.getOperand(i);
                processExpression(tid, newWx, lp);
            }
        }
        // OR 条件目前不支持
        else if (wx.getOperator().equals("OR")) {
            throw new simpledb.ParsingException("OR 表达式目前暂不支持。");
        }
        // 其他普通二元比较操作
        else {
            @SuppressWarnings("unchecked")
            List<ZExp> ops = wx.getOperands();

            // 只支持两个操作数的基本比较表达式 A op B
            if (ops.size() != 2) {
                throw new simpledb.ParsingException("仅支持形如 A op B 的简单二元表达式。");
            }

            boolean isJoin = false;
            Predicate.Op op = getOp(wx.getOperator());

            // 判断两个操作数是否为常量
            boolean op1const = ops.get(0) instanceof ZConstant;
            boolean op2const = ops.get(1) instanceof ZConstant;

            // 如果两个都是列名，则认为这是一个 JOIN 条件
            if (op1const && op2const) {
                isJoin = ((ZConstant) ops.get(0)).getType() == ZConstant.COLUMNNAME
                         && ((ZConstant) ops.get(1)).getType() == ZConstant.COLUMNNAME;
            }
            // 如果任一是子查询（ZQuery），也认为是一个 JOIN
            else if (ops.get(0) instanceof ZQuery || ops.get(1) instanceof ZQuery) {
                isJoin = true;
            }
            // 如果是嵌套表达式，抛出异常
            else if (ops.get(0) instanceof ZExpression || ops.get(1) instanceof ZExpression) {
                throw new simpledb.ParsingException("仅支持形如 A op B 的简单二元表达式，其中 A 或 B 是字段、常量或子查询。");
            } else {
                isJoin = false;
            }

            // 处理 JOIN 条件
            if (isJoin) {
                String tab1field = "", tab2field = "";

                // 左边操作数如果是常量（列名）
                if (!op1const) { // left 是嵌套查询
                    // TODO: 不支持虚拟表生成
                } else {
                    tab1field = ((ZConstant) ops.get(0)).getValue();
                }

                // 右边操作数如果是嵌套查询（ZQuery），则解析为子查询
                if (!op2const) {
                    LogicalPlan sublp = parseQueryLogicalPlan(tid, (ZQuery) ops.get(1));
                    OpIterator pp = sublp.physicalPlan(tid, TableStats.getStatsMap(), explain);
                    lp.addJoin(tab1field, pp, op); // 添加带物理迭代器的 Join
                } else {
                    tab2field = ((ZConstant) ops.get(1)).getValue();
                    lp.addJoin(tab1field, tab2field, op); // 添加普通 Join
                }

            }
            // 处理 SELECT 条件（即 Filter）
            else {
                String column;
                String compValue;
                ZConstant op1 = (ZConstant) ops.get(0);
                ZConstant op2 = (ZConstant) ops.get(1);

                // 判断哪个是列名，哪个是常量值
                if (op1.getType() == ZConstant.COLUMNNAME) {
                    column = op1.getValue();
                    compValue = op2.getValue();
                } else {
                    column = op2.getValue();
                    compValue = op1.getValue();
                }

                // 添加过滤条件到逻辑计划中
                lp.addFilter(column, op, compValue);
            }
        }
    }

    /**
     * 解析 SQL 查询，构建逻辑计划（LogicalPlan）
     *
     * @param tid 当前事务 ID
     * @param q   ZQuery 对象，表示用户输入的 SQL 查询
     * @return 构建好的 LogicalPlan 对象
     * @throws IOException               IO 异常
     * @throws Zql.ParseException        ZQL 解析异常
     * @throws simpledb.ParsingException 自定义解析异常
     */
    public LogicalPlan parseQueryLogicalPlan(TransactionId tid, ZQuery q) throws IOException, Zql.ParseException,
                                                                         simpledb.ParsingException {

        // 1. 解析 FROM 子句，生成 TableScan 节点
        @SuppressWarnings("unchecked")
        List<ZFromItem> from = q.getFrom();
        LogicalPlan lp = new LogicalPlan();
        lp.setQuery(q.toString());

        // 遍历 FROM 中的每个表项
        for (int i = 0; i < from.size(); i++) {
            ZFromItem fromIt = from.get(i);
            try {
                // 获取表在数据库中的唯一 ID
                int id = Database.getCatalog().getTableId(fromIt.getTable());
                String name;

                // 如果有别名使用别名，否则使用原表名
                if (fromIt.getAlias() != null)
                    name = fromIt.getAlias();
                else
                    name = fromIt.getTable();

                // 添加扫描节点到逻辑计划中
                lp.addScan(id, name);

                // TODO: 如何处理子查询？

            } catch (NoSuchElementException e) {
                e.printStackTrace();
                throw new simpledb.ParsingException("表 " + fromIt.getTable() + " 不存在于数据库中");
            }
        }

        // 2. 解析 WHERE 子句，创建 Filter 或 Join 节点
        ZExp w = q.getWhere();
        if (w != null) {
            if (!(w instanceof ZExpression)) {
                throw new simpledb.ParsingException("嵌套查询目前不支持。");
            }
            ZExpression wx = (ZExpression) w;
            processExpression(tid, wx, lp); // 处理表达式逻辑
        }

        // 3. 查找 GROUP BY 字段
        ZGroupBy gby = q.getGroupBy();
        String groupByField = null;
        if (gby != null) {
            @SuppressWarnings("unchecked")
            List<ZExp> gbs = gby.getGroupBy();
            if (gbs.size() > 1) {
                throw new simpledb.ParsingException("最多支持一个 GROUP BY 表达式字段。");
            }
            if (gbs.size() == 1) {
                ZExp gbe = gbs.get(0);
                if (!(gbe instanceof ZConstant)) {
                    throw new simpledb.ParsingException("复杂 GROUP BY 表达式 (" + gbe + ") 不被支持。");
                }
                groupByField = ((ZConstant) gbe).getValue();
                System.out.println("GROUP BY 字段 : " + groupByField);
            }
        }

        // 4. 遍历 SELECT 列表，提取聚合字段并验证查询合法性
        @SuppressWarnings("unchecked")
        List<ZSelectItem> selectList = q.getSelect();
        String aggField = null;
        String aggFun = null;

        for (int i = 0; i < selectList.size(); i++) {
            ZSelectItem si = selectList.get(i);

            // 不支持复杂的 SELECT 表达式
            if (si.getAggregate() == null && (si.isExpression() && !(si.getExpression() instanceof ZConstant))) {
                throw new simpledb.ParsingException("SELECT 列表中的复杂表达式不被支持。");
            }

            // 如果是聚合函数
            if (si.getAggregate() != null) {
                if (aggField != null) {
                    throw new simpledb.ParsingException("不支持对多个字段进行聚合。");
                }
                // 提取聚合字段和函数名称
                aggField = ((ZConstant) ((ZExpression) si.getExpression()).getOperand(0)).getValue();
                aggFun = si.getAggregate();
                System.out.println("聚合字段为：" + aggField + "，聚合函数为：" + aggFun);
                lp.addProjectField(aggField, aggFun); // 添加聚合投影字段
            } else {
                // 检查非聚合字段是否出现在 GROUP BY 中
                if (groupByField != null
                    && !(groupByField.equals(si.getTable() + "." + si.getColumn()) || groupByField.equals(si
                        .getColumn()))) {
                    throw new simpledb.ParsingException("非聚合字段 " + si.getColumn() + " 未出现在 GROUP BY 列表中。");
                }
                lp.addProjectField(si.getTable() + "." + si.getColumn(), null); // 添加普通投影字段
            }
        }

        // 必须有聚合函数才能使用 GROUP BY
        if (groupByField != null && aggFun == null) {
            throw new simpledb.ParsingException("GROUP BY 必须配合聚合函数使用。");
        }

        // 添加聚合操作到逻辑计划
        if (aggFun != null) {
            lp.addAggregate(aggFun, aggField, groupByField);
        }

        // 5. 处理 ORDER BY 排序
        if (q.getOrderBy() != null) {
            @SuppressWarnings("unchecked")
            List<ZOrderBy> obys = q.getOrderBy();
            if (obys.size() > 1) {
                throw new simpledb.ParsingException("多属性排序 ORDER BY 不被支持。");
            }
            ZOrderBy oby = obys.get(0);
            if (!(oby.getExpression() instanceof ZConstant)) {
                throw new simpledb.ParsingException("复杂排序条件不被支持。");
            }
            ZConstant f = (ZConstant) oby.getExpression();

            // 添加排序操作到逻辑计划
            lp.addOrderBy(f.getValue(), oby.getAscOrder());
        }

        return lp;
    }

    // 当前事务对象
    private Transaction curtrans    = null;
    // 是否处于用户事务中
    private boolean     inUserTrans = false;

    /**
     * 处理 SQL 查询语句，生成查询对象 Query 并设置物理执行计划
     *
     * @param s   ZQuery 对象，表示用户输入的 SQL 查询
     * @param tId 当前事务 ID
     * @return 构造好的 Query 对象
     * @throws IOException               IO 异常
     * @throws simpledb.ParsingException 自定义解析异常
     * @throws Zql.ParseException        ZQL 解析异常
     */
    public Query handleQueryStatement(ZQuery s, TransactionId tId) throws IOException, simpledb.ParsingException,
                                                                  Zql.ParseException {

        Query query = new Query(tId);

        // 解析 SQL 查询，生成逻辑计划
        LogicalPlan lp = parseQueryLogicalPlan(tId, s);

        // 将逻辑计划转换为物理执行计划
        OpIterator physicalPlan = lp.physicalPlan(tId, TableStats.getStatsMap(), explain);
        query.setPhysicalPlan(physicalPlan);
        query.setLogicalPlan(lp);

        // 如果启用了 explain 模式，输出执行计划树
        if (physicalPlan != null) {
            Class<?> c;
            try {
                c = Class.forName("simpledb.optimizer.OperatorCardinality");

                Class<?> p = Operator.class;
                Class<?> h = Map.class;

                java.lang.reflect.Method m = c.getMethod("updateOperatorCardinality", p, h, h);

                System.out.println("查询执行计划为：");
                m.invoke(null, physicalPlan, lp.getTableAliasToIdMapping(), TableStats.getStatsMap());

                c = Class.forName("simpledb.optimizer.QueryPlanVisualizer");
                m = c.getMethod("printQueryPlanTree", OpIterator.class, System.out.getClass());
                m.invoke(c.newInstance(), physicalPlan, System.out);
            } catch (ClassNotFoundException | SecurityException ignored) {
            } catch (NoSuchMethodException | InstantiationException | InvocationTargetException
                    | IllegalAccessException | IllegalArgumentException e) {
                e.printStackTrace();
            }
        }

        return query;
    }

    /**
     * 处理 SQL INSERT 插入语句，构建插入操作的查询计划（Query）
     *
     * @param s   ZInsert 对象，表示用户输入的 INSERT 语句
     * @param tId 当前事务 ID
     * @return 构建好的 Query 对象，包含插入操作的物理执行计划
     * @throws DbException               数据库异常
     * @throws IOException               IO 异常
     * @throws simpledb.ParsingException 自定义解析异常
     * @throws Zql.ParseException        ZQL 解析异常
     */
    public Query handleInsertStatement(ZInsert s, TransactionId tId) throws DbException, IOException,
                                                                    simpledb.ParsingException, Zql.ParseException {

        int tableId;
        try {
            // 获取目标表的唯一 ID
            tableId = Database.getCatalog().getTableId(s.getTable());
        } catch (NoSuchElementException e) {
            throw new simpledb.ParsingException("未知表：" + s.getTable());
        }

        // 获取目标表的元信息（TupleDesc）
        TupleDesc td = Database.getCatalog().getTupleDesc(tableId);

        // 创建一个空元组用于插入
        Tuple t = new Tuple(td);
        int i = 0;
        OpIterator newTups;

        // 判断是 VALUES 形式还是 SELECT 形式的插入
        if (s.getValues() != null) {
            @SuppressWarnings("unchecked")
            List<ZExp> values = s.getValues();

            // 检查字段数量是否匹配
            if (td.numFields() != values.size()) {
                throw new simpledb.ParsingException("INSERT 语句中的字段数与表 " + s.getTable() + " 不一致。");
            }

            for (ZExp e : values) {
                if (!(e instanceof ZConstant)) {
                    throw new simpledb.ParsingException("INSERT 语句中不允许使用复杂表达式。");
                }
                ZConstant zc = (ZConstant) e;

                // 根据值类型创建对应的字段并设置到元组中
                if (zc.getType() == ZConstant.NUMBER) {
                    if (td.getFieldType(i) != Type.INT_TYPE) {
                        throw new simpledb.ParsingException("值 " + zc.getValue() + " 是数字，但期望字符串类型。");
                    }
                    IntField f = new IntField(new Integer(zc.getValue()));
                    t.setField(i, f);
                } else if (zc.getType() == ZConstant.STRING) {
                    if (td.getFieldType(i) != Type.STRING_TYPE) {
                        throw new simpledb.ParsingException("值 " + zc.getValue() + " 是字符串，但期望数字类型。");
                    }
                    StringField f = new StringField(zc.getValue(), Type.STRING_LEN);
                    t.setField(i, f);
                } else {
                    throw new simpledb.ParsingException("只支持字符串和整型字段。");
                }

                i++;
            }

            // 将构造好的元组放入迭代器中
            List<Tuple> tups = new ArrayList<>();
            tups.add(t);
            newTups = new TupleArrayIterator(tups);

        } else {
            // 如果是 SELECT 形式的插入，则解析子查询生成逻辑计划
            ZQuery zq = s.getQuery();
            LogicalPlan lp = parseQueryLogicalPlan(tId, zq);
            newTups = lp.physicalPlan(tId, TableStats.getStatsMap(), explain);
        }

        // 构造 Query 对象，并设置插入操作的物理执行计划
        Query insertQ = new Query(tId);
        insertQ.setPhysicalPlan(new Insert(tId, newTups, tableId));
        return insertQ;
    }

    /**
     * 处理 SQL DELETE 删除语句，构建删除操作的查询计划（Query）
     *
     * @param s   ZDelete 对象，表示用户输入的 DELETE 语句
     * @param tid 当前事务 ID
     * @return 构建好的 Query 对象，包含删除操作的物理执行计划
     * @throws simpledb.ParsingException 自定义解析异常
     * @throws IOException               IO 异常
     * @throws ParseException            ZQL 解析异常
     */
    public Query handleDeleteStatement(ZDelete s, TransactionId tid) throws simpledb.ParsingException, IOException,
                                                                    ParseException {

        int id;
        try {
            // 获取目标表的唯一 ID
            id = Database.getCatalog().getTableId(s.getTable());
        } catch (NoSuchElementException e) {
            throw new simpledb.ParsingException("未知表：" + s.getTable());
        }

        String name = s.getTable();
        Query sdbq = new Query(tid);

        // 创建逻辑计划对象
        LogicalPlan lp = new LogicalPlan();
        lp.setQuery(s.toString());

        // 添加扫描节点
        lp.addScan(id, name);

        // 如果有 WHERE 条件，解析条件表达式
        if (s.getWhere() != null)
            processExpression(tid, (ZExpression) s.getWhere(), lp);

        // 添加一个通配符投影字段（为了触发遍历所有符合条件的记录）
        lp.addProjectField("null.*", null);

        // 构造删除操作的物理执行计划
        OpIterator op = new Delete(tid, lp.physicalPlan(tid, TableStats.getStatsMap(), false));
        sdbq.setPhysicalPlan(op);

        return sdbq;
    }

    /**
     * 处理事务控制语句：COMMIT / ROLLBACK / SET TRANSACTION
     *
     * @param s 表示事务控制的语句对象
     * @throws IOException               IO 异常
     * @throws simpledb.ParsingException 自定义解析异常
     */
    public void handleTransactStatement(ZTransactStmt s) throws IOException, simpledb.ParsingException {
        switch (s.getStmtType()) {
            case "COMMIT":
                if (curtrans == null)
                    throw new ParsingException("当前没有运行的事务");
                curtrans.commit(); // 提交事务
                System.out.println("事务 " + curtrans.getId().getId() + " 已提交。");
                curtrans = null;
                inUserTrans = false;
                break;
            case "ROLLBACK":
                if (curtrans == null)
                    throw new ParsingException("当前没有运行的事务");
                curtrans.abort(); // 回滚事务
                System.out.println("事务 " + curtrans.getId().getId() + " 已回滚。");
                curtrans = null;
                inUserTrans = false;
                break;
            case "SET TRANSACTION":
                if (curtrans != null)
                    throw new ParsingException("必须先提交或回滚当前事务才能开始新事务");
                curtrans = new Transaction();
                curtrans.start(); // 启动事务
                inUserTrans = true;
                System.out.println("已启动新事务，tid = " + curtrans.getId().getId());
                break;
            default:
                throw new ParsingException("不支持的操作");
        }
    }

    /**
     * 根据 SQL 字符串生成对应的逻辑查询计划（LogicalPlan）
     *
     * @param tid 当前事务 ID
     * @param s   SQL 查询语句字符串
     * @return 解析后的逻辑查询计划对象
     * @throws simpledb.ParsingException 自定义解析异常
     * @throws IOException               IO 异常
     */
    public LogicalPlan generateLogicalPlan(TransactionId tid, String s) throws simpledb.ParsingException, IOException {
        // 将 SQL 字符串转换为字节输入流
        ByteArrayInputStream bis = new ByteArrayInputStream(s.getBytes());
        ZqlParser p = new ZqlParser(bis);

        try {
            // 读取一条 SQL 语句
            ZStatement stmt = p.readStatement();

            // 如果是 SELECT 查询语句，则调用 parseQueryLogicalPlan 方法解析
            if (stmt instanceof ZQuery) {
                return parseQueryLogicalPlan(tid, (ZQuery) stmt);
            }
        } catch (Zql.ParseException e) {
            throw new simpledb.ParsingException("无效的 SQL 表达式: \n\t" + e);
        }

        // 不支持其他类型的语句（如 UPDATE、CREATE TABLE 等）
        throw new simpledb.ParsingException("无法为表达式生成逻辑计划: " + s);
    }

    /**
     * 设置当前运行的事务对象
     *
     * @param t 要设置的事务对象
     */
    public void setTransaction(Transaction t) {
        curtrans = t;
    }

    /**
     * 获取当前运行的事务对象
     *
     * @return 当前事务对象
     */
    public Transaction getTransaction() {
        return curtrans;
    }

    /**
     * 处理下一条 SQL 语句（接受字符串形式的 SQL）
     *
     * @param s SQL 语句字符串
     */
    public void processNextStatement(String s) {
        processNextStatement(new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 处理下一条 SQL 语句（接受 InputStream 输入）
     *
     * @param is SQL 输入流
     */
    public void processNextStatement(InputStream is) {
        try {
            // 创建 ZQL 解析器并读取 SQL 语句
            ZqlParser p = new ZqlParser(is);
            ZStatement s = p.readStatement();

            Query query = null;

            // 判断是否是事务控制语句（BEGIN / COMMIT / ROLLBACK）
            if (s instanceof ZTransactStmt)
                handleTransactStatement((ZTransactStmt) s); // 处理事务控制语句
            else {
                // 如果不在用户事务中，则自动开启新事务
                if (!this.inUserTrans) {
                    curtrans = new Transaction();
                    curtrans.start(); // 启动事务
                    System.out.println("已启动新事务，tid = " + curtrans.getId().getId());
                }

                try {
                    // 根据语句类型分别处理插入、删除和查询操作
                    if (s instanceof ZInsert)
                        query = handleInsertStatement((ZInsert) s, curtrans.getId()); // 插入
                    else if (s instanceof ZDelete)
                        query = handleDeleteStatement((ZDelete) s, curtrans.getId()); // 删除
                    else if (s instanceof ZQuery)
                        query = handleQueryStatement((ZQuery) s, curtrans.getId()); // 查询
                    else {
                        System.out.println("不支持的语句类型：" + s + "\n-- 目前只支持：INSERT、DELETE、SELECT 和事务控制语句");
                    }

                    // 如果构建了查询对象，则执行它
                    if (query != null)
                        query.execute();

                    // 如果不是用户事务，则自动提交
                    if (!inUserTrans && curtrans != null) {
                        curtrans.commit(); // 提交事务
                        System.out.println("事务 " + curtrans.getId().getId() + " 已提交。");
                    }

                } catch (Throwable a) {
                    // 出现异常时回滚事务
                    if (curtrans != null) {
                        curtrans.abort(); // 回滚事务
                        System.out.println("事务 " + curtrans.getId().getId() + " 因错误被终止。");
                    }
                    this.inUserTrans = false;

                    // 抛出特定异常
                    if (a instanceof simpledb.ParsingException || a instanceof Zql.ParseException)
                        throw new ParsingException((Exception) a);
                    if (a instanceof Zql.TokenMgrError)
                        throw (Zql.TokenMgrError) a;
                    throw new DbException(a.getMessage());
                } finally {
                    // 如果不是用户事务，清空当前事务引用
                    if (!inUserTrans)
                        curtrans = null;
                }
            }

        } catch (IOException | DbException e) {
            e.printStackTrace();
        } catch (simpledb.ParsingException e) {
            System.out.println("无效的 SQL 表达式: \n\t" + e.getMessage());
        } catch (ParseException | TokenMgrError e) {
            System.out.println("无效的 SQL 表达式: \n\t" + e);
        }
    }

    // 支持的 SQL 命令关键词列表
    public static final String[] SQL_COMMANDS = { "select", "from", "where", "group by", "max(", "min(", "avg(",
            "count", "rollback", "commit", "insert", "delete", "values", "into" };

    /**
     * 主方法入口，用于启动解析器程序
     *
     * @param argv 命令行参数
     * @throws IOException IO 异常
     */
    public static void main(String[] argv) throws IOException {

        if (argv.length < 1 || argv.length > 4) {
            System.out.println("参数数量错误。\n" + usage);
            System.exit(0);
        }

        Parser p = new Parser();

        p.start(argv); // 启动解析器
    }

    // 常量定义
    static final String usage      = "Usage: parser catalogFile [-explain] [-f queryFile]";
    static final int    SLEEP_TIME = 1000;

    /**
     * 关闭解析器时输出提示信息
     */
    protected void shutdown() {
        System.out.println("Bye");
    }

    /**
     * 是否处于交互模式（默认为 true）
     */
    protected boolean interactive = true;

    /**
     * 启动数据库解析器程序，加载模式并处理 SQL 查询语句
     *
     * @param argv 命令行参数数组
     * @throws IOException IO 异常
     */
    protected void start(String[] argv) throws IOException {
        // 首先将表加载到数据库中
        Database.getCatalog().loadSchema(argv[0]);

        // 计算各表的统计信息（用于查询优化）
        TableStats.computeStatistics();

        String queryFile = null;

        // 解析命令行参数
        if (argv.length > 1) {
            for (int i = 1; i < argv.length; i++) {
                if (argv[i].equals("-explain")) {
                    explain = true;
                    System.out.println("已启用解释模式。");
                } else if (argv[i].equals("-f")) {
                    interactive = false; // 设置为非交互模式
                    if (++i == argv.length) {
                        System.out.println("在 -f 参数后需要提供文件名\n" + usage);
                        System.exit(0);
                    }
                    queryFile = argv[i]; // 获取查询文件路径
                } else {
                    System.out.println("未知参数 " + argv[i] + "\n " + usage);
                }
            }
        }

        // 如果是非交互模式，从指定文件中读取 SQL 并执行
        if (!interactive) {
            try {
                // 模拟短暂延迟（可能是为了展示效果）
                try {
                    Thread.sleep(SLEEP_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                long startTime = System.currentTimeMillis();

                // 处理 SQL 文件中的下一条语句
                processNextStatement(new FileInputStream(queryFile));

                long time = System.currentTimeMillis() - startTime;

                // 输出执行耗时
                System.out.printf("----------------\n%.2f 秒\n\n", ((double) time / 1000.0));

                System.out.println("按回车键退出");
                System.in.read(); // 等待用户输入

                this.shutdown(); // 关闭程序
            } catch (FileNotFoundException e) {
                System.out.println("无法找到查询文件：" + queryFile);
                e.printStackTrace();
            }
        } else { // 如果是交互模式，启动控制台输入提示
            ConsoleReader reader = new ConsoleReader();

            // 添加简单的 SQL 自动补全功能
            ArgumentCompletor completor = new ArgumentCompletor(new SimpleCompletor(SQL_COMMANDS));
            completor.setStrict(false); // 支持模糊匹配
            reader.addCompletor(completor);

            StringBuilder buffer = new StringBuilder();

            // 示例 SQL 语句（供参考）
            /**
             * 查询计划树示例：
             *              π(d.f2,d1.f2),card:1
             *              |
             *              o(d.f2),card:1
             *              |
             *           ⨝(hash)(d.f1=d1.f1),card:1
             *    __________|___________
             *    |                    |
             *    σ(d.f2>20),card:1    |
             *    |                    |
             *  scan(data d)         scan(data d1)
             *
             */
            String line = "select d.f2, d1.f2 from data d, data d1 where d.f1 = d1.f1 and d.f2 > 20 order by d.f2 desc;";
            boolean quit = false;

            while (!quit) {
                // 按分号 ';' 分割多条 SQL 语句
                while (line.indexOf(';') >= 0) {
                    int split = line.indexOf(';');
                    buffer.append(line, 0, split + 1);
                    String cmd = buffer.toString().trim();

                    // 去掉最后的分号并重新添加
                    cmd = cmd.substring(0, cmd.length() - 1).trim() + ";";

                    byte[] statementBytes = cmd.getBytes(StandardCharsets.UTF_8);

                    // 判断是否是退出命令
                    if (cmd.equalsIgnoreCase("quit;") || cmd.equalsIgnoreCase("exit;")) {
                        shutdown();
                        quit = true;
                        break;
                    }

                    long startTime = System.currentTimeMillis();

                    // 执行当前 SQL 语句
                    processNextStatement(new ByteArrayInputStream(statementBytes));

                    long time = System.currentTimeMillis() - startTime;

                    // 输出执行时间
                    System.out.printf("----------------\n%.2f 秒\n\n", ((double) time / 1000.0));

                    // 继续处理后续 SQL 语句
                    line = line.substring(split + 1);
                    buffer = new StringBuilder();
                }

                // 如果还有未处理的内容，继续拼接
                if (line.length() > 0) {
                    buffer.append(line);
                    buffer.append("\n");
                }
            }
        }
    }
}

/**
 * TupleArrayIterator 是一个实现了 OpIterator 接口的迭代器类，
 * 用于遍历一组元组。
 */
class TupleArrayIterator implements OpIterator {

    private static final long serialVersionUID = 1L;

    // 存储元组的列表
    final List<Tuple>         tups;

    // 实际使用的迭代器
    Iterator<Tuple>           it               = null;

    /**
     * 构造函数
     *
     * @param tups 要遍历的元组列表
     */
    public TupleArrayIterator(List<Tuple> tups) {
        this.tups = tups;
    }

    /**
     * 打开迭代器，准备开始遍历
     */
    public void open() {
        it = tups.iterator();
    }

    /**
     * 判断是否有下一个元素
     *
     * @return 是否存在下一个元素
     */
    public boolean hasNext() {
        return it.hasNext();
    }

    /**
     * 获取下一个元素
     *
     * @return 下一个元组
     * @throws NoSuchElementException 如果没有更多元素
     */
    public Tuple next() throws NoSuchElementException {
        return it.next();
    }

    /**
     * 重置迭代器，回到起始位置
     */
    public void rewind() {
        it = tups.iterator();
    }

    /**
     * 获取该迭代器对应的元组描述信息
     *
     * @return 元组描述对象
     */
    public TupleDesc getTupleDesc() {
        return tups.get(0).getTupleDesc();
    }

    /**
     * 关闭迭代器
     */
    public void close() {
    }
}
